package scheme;
import accessstruct.AccessControlParameter;
import accessstruct.Tool;
import accessstruct.lsss.LSSSLW10Engine;
import accessstruct.lsss.LSSSPolicyEngine;
import accessstruct.parser.ParserUtils;
import accessstruct.parser.PolicySyntaxException;
import algebra.algorithms.AttrTrans;
import it.unisa.dia.gas.jpbc.Element;
import it.unisa.dia.gas.jpbc.Pairing;
import it.unisa.dia.gas.plaf.jpbc.pairing.PairingFactory;
import util.AccessPolicyExamples;
import util.ConfigFileName;

import javax.swing.text.Style;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Map;
import java.util.Properties;

/**
 * @Classname Testdemo
 * @Description 基本加解密测试
 * @Created by kevin
 */
public class Testdemo {

    public static void CAsetup(ConfigFileName config) throws Exception{

        Properties AAskProp = new Properties();
        Tool.storePropToFile(AAskProp, config.getAASKFileName());
        Properties AApkProp = new Properties();
        Tool.storePropToFile(AApkProp, config.getAAPKFileName());
        Properties CSPUAkProp = new Properties();
        Tool.storePropToFile(CSPUAkProp, config.getCSPUAKFileName());
        Properties UserTKProp = new Properties();
        Tool.storePropToFile(UserTKProp, config.getTKFileName());
        Properties CSPUAKProp = new Properties();
        Tool.storePropToFile(CSPUAKProp, config.getCSPUAKFileName());
        Properties RSUTKProp = new Properties();
        Tool.storePropToFile(RSUTKProp, config.getTKFileName());
        Properties ctProp = new Properties();
        Tool.storePropToFile(ctProp, config.getCtFileName());
        Properties CApkProp = new Properties();//
        Tool.storePropToFile(RSUTKProp, config.getTKFileName());
        Properties USKPRrop = new Properties();
        Tool.storePropToFile(USKPRrop, config.getUserSKFileName());

        Pairing bp = PairingFactory.getPairing(config.getPairingParametersFileName());//
        Element g = bp.getG1().newRandomElement().getImmutable();//
        Element w = bp.getG1().newRandomElement().getImmutable();//
        Element v = bp.getG1().newRandomElement().getImmutable();//
        CApkProp.setProperty("g", Base64.getEncoder().withoutPadding().encodeToString(g.toBytes()));//
        CApkProp.setProperty("w", Base64.getEncoder().withoutPadding().encodeToString(w.toBytes()));//
        CApkProp.setProperty("v", Base64.getEncoder().withoutPadding().encodeToString(v.toBytes()));//
        Tool.storePropToFile(CApkProp, config.getGPFileName());
        System.out.println("系统初始化结束");

    }
    public static void AAsetup(ConfigFileName config) {
        /*********************************************获取公共参数*******************************************************/
        //准备工作获取公参
        Pairing bp = PairingFactory.getPairing(config.getPairingParametersFileName());//
        Properties AAskProp = Tool.loadPropFromFile(config.getAASKFileName());//
        Properties AApkProp = Tool.loadPropFromFile(config.getAAPKFileName());//
        Properties GPProp = Tool.loadPropFromFile(config.getGPFileName());//
        //获取g
        String gString = GPProp.getProperty("g");//
        Element g = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(gString)).getImmutable();//
        /************************************************计算私钥公钥*****************************************************/
        //选取AA私钥和公钥
        //AA私钥
        Element alpha = bp.getZr().newRandomElement().getImmutable();//alpha
        Element beta = bp.getZr().newRandomElement().getImmutable();//beta
        //为每个属性选择密钥 AttrSK 作为私钥的一部分

        //AA公钥
        Element egg_beta = bp.pairing(g,g).powZn(beta).getImmutable();//
        Element g_alpha = g.powZn(alpha).getImmutable();//

        //生成系统公钥和私钥并存入prop文件之中
        //存储私钥
        AAskProp.setProperty("alpha", Base64.getEncoder().withoutPadding().encodeToString(alpha.toBytes()));//
        AAskProp.setProperty("beta", Base64.getEncoder().withoutPadding().encodeToString(beta.toBytes()));//
        AApkProp.setProperty("egg_beta", Base64.getEncoder().withoutPadding().encodeToString(egg_beta.toBytes()));//
        AApkProp.setProperty("g_alpha", Base64.getEncoder().withoutPadding().encodeToString(g_alpha.toBytes()));//

        Tool.storePropToFile(AAskProp, config.getAASKFileName());//
        Tool.storePropToFile(AApkProp, config.getAAPKFileName());//

        System.out.println("AA初始化结束");
    }
    public static long AAkeygen(ConfigFileName config , String userId, String[] userAttList) throws NoSuchAlgorithmException {
        /********************************************获取公共参数和AA私钥*************************************************/
        //准备工作
        Pairing bp = PairingFactory.getPairing(config.getPairingParametersFileName());//

        //读取系统公共参数GP
        Properties GPProp = Tool.loadPropFromFile(config.getGPFileName());//
        String gString = GPProp.getProperty("g");//
        Element g = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(gString)).getImmutable();//
        String wString = GPProp.getProperty("w");//
        Element w = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(wString)).getImmutable();//
        String vString = GPProp.getProperty("v");//
        Element v = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(vString)).getImmutable();//

        //读取AA公钥 g^alpha
        Properties AApkProp = Tool.loadPropFromFile(config.getAAPKFileName());//
        String g_alphaString = AApkProp.getProperty("g_alpha");//
        Element g_alpha = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(g_alphaString)).getImmutable();//

        //读取每个AA私钥AA_SK_alpha_aid  AA_SK_beta_aid
        Properties AAskProp = Tool.loadPropFromFile(config.getAASKFileName());//
        String alphaString = AAskProp.getProperty("alpha");//
        Element alpha = bp.getZr().newElementFromBytes(Base64.getDecoder().decode(alphaString)).getImmutable();//
        String betaString = AAskProp.getProperty("beta");//
        Element beta = bp.getZr().newElementFromBytes(Base64.getDecoder().decode(betaString)).getImmutable();//

        //加载用户密钥存储工具
        Properties UserSkProp = Tool.loadPropFromFile(config.getUserSKFileName());

        //开始申城属性密钥
        long startTime = System.currentTimeMillis();
        //AA随机选取r_aid
        Element theta= bp.getZr().newRandomElement().getImmutable();//
        Element r= bp.getZr().newRandomElement().getImmutable();//
        //生成属性密钥
        Element K = g.powZn(beta.mul(alpha.invert())).mul(w.powZn(r)).powZn(theta.invert()).getImmutable();//正确
        Element L_0 = g.powZn(r).powZn(theta.invert()).getImmutable();//正确
        Element L_1 = g_alpha.powZn(r).powZn(theta.invert()).getImmutable();;//正确


        //开始存储密钥
        //参数r_aid
        UserSkProp.setProperty("theta", Base64.getEncoder().withoutPadding().encodeToString(theta.toBytes()));//
        //用户属性密钥
        UserSkProp.setProperty("K", Base64.getEncoder().withoutPadding().encodeToString(K.toBytes()));//
        UserSkProp.setProperty("L_0", Base64.getEncoder().withoutPadding().encodeToString(L_0.toBytes()));//
        UserSkProp.setProperty("L_1", Base64.getEncoder().withoutPadding().encodeToString(L_1.toBytes()));//

        //对用户的每一个属性值进行密钥生成Ki
        for (String att :userAttList) {
            //属性密钥
            byte[] attHash = Tool.sha1(att);
            Element Si = bp.getZr().newElementFromHash(attHash, 0, attHash.length).getImmutable();
            Element K_att = g.powZn(r).powZn(Si).mul(v.powZn(alpha).powZn(r)).powZn(theta.invert()).getImmutable();
            //存储属性密钥
            UserSkProp.setProperty("K_"+att, Base64.getEncoder().withoutPadding().encodeToString(K_att.toBytes()));
        }
        long endTime = System.currentTimeMillis();
        Tool.storePropToFile(UserSkProp, config.getUserSKFileName());
        System.out.println("密钥生成结束");
        return endTime - startTime;

    }
    public static long Encrypt(ConfigFileName config,  String[] AccessAtt, String userID,int aaNums,int [][] accessPolicyMatrix,String[] rhos,Element Message, AccessControlParameter test) throws NoSuchAlgorithmException, PolicySyntaxException {

        /*********************************************获取公共参数*******************************************************/
        //准备工作
        Pairing bp = PairingFactory.getPairing(config.getPairingParametersFileName());
        //获取全局参数GP
        Properties CAPKProp = Tool.loadPropFromFile(config.getGPFileName());
        Properties AAPKProp = Tool.loadPropFromFile(config.getAAPKFileName());
        Properties ctProp = Tool.loadPropFromFile(config.getCtFileName());
        String gString = CAPKProp.getProperty("g");
        Element g = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(gString)).getImmutable();
        String wString = CAPKProp.getProperty("w");
        Element w = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(wString)).getImmutable();
        String vString = CAPKProp.getProperty("v");
        Element v = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(vString)).getImmutable();

        /**********************************************制定访问策略*****************************************************/
        //随选秘密值s
        Element s = bp.getZr().newRandomElement().getImmutable();

        //进行秘密共享
        Map<String, Element> lambdas =LSSSPolicyEngine.secretSharing(bp,s,test);

        /************************************************计算密文*******************************************************/
        long offlineEncryptStartTime = System.currentTimeMillis();
        //计算C
        String eggbetaString = AAPKProp.getProperty("egg_beta");
        Element eggbeta = bp.getGT().newElementFromBytes(Base64.getDecoder().decode(eggbetaString)).getImmutable();
        Element C = Message.mul(eggbeta.powZn(s)).getImmutable();
        System.out.println("C= " +C);
        ctProp.setProperty("C", Base64.getEncoder().withoutPadding().encodeToString(C.toBytes()));

        //计算C_0;
        Element C_0 = g.powZn(s).getImmutable();
        ctProp.setProperty("C_0", Base64.getEncoder().withoutPadding().encodeToString(C_0.toBytes()));

        //计算C_1
        String galphaString = AAPKProp.getProperty("g_alpha");
        Element g_alpha = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(galphaString)).getImmutable();
        Element C_1 = g_alpha.powZn(s).getImmutable();
        ctProp.setProperty("C_1", Base64.getEncoder().withoutPadding().encodeToString(C_1.toBytes()));

        for (Map.Entry<String, Element> entry : lambdas.entrySet()) {
            String att = entry.getKey();
            //随机选取l个参数z_i \in Zp
            Element z_i = bp.getZr().newRandomElement().getImmutable();
            //密文
            byte[] attHash = Tool.sha1(att);
            Element rhoi = bp.getZr().newElementFromHash(attHash, 0, attHash.length).getImmutable();

            Element C_i_1 = w.powZn(lambdas.get(att)).mul(v.invert().powZn(z_i)).getImmutable();
            Element C_i_2 = g.powZn(lambdas.get(att)).mul(g.invert().powZn(z_i).powZn(rhoi)).getImmutable();
            Element C_i_3 = g.powZn(z_i).getImmutable();

            ctProp.setProperty("C_" + att + "_1", Base64.getEncoder().withoutPadding().encodeToString(C_i_1.toBytes()));
            ctProp.setProperty("C_" + att + "_2", Base64.getEncoder().withoutPadding().encodeToString(C_i_2.toBytes()));
            ctProp.setProperty("C_" + att + "_3", Base64.getEncoder().withoutPadding().encodeToString(C_i_3.toBytes()));
        }
        //存储秘密值
        Properties sProp = Tool.loadPropFromFile(config.getSecretFileName());
        sProp.setProperty("s_"+userID, Base64.getEncoder().withoutPadding().encodeToString(s.toBytes()));
        Tool.storePropToFile(sProp, config.getSecretFileName());
        Tool.storePropToFile(ctProp, config.getCtFileName());
        System.out.println("加密结束");
        return offlineEncryptStartTime;
    }
    public static Element Decrypt(ConfigFileName config, String[] userAttList, String userId, int aaNums,int[][] AccessPolicy,String[] rhos, AccessControlParameter test)
            throws Exception {
        /*********************************************获取公共参数*******************************************************/
        //准备工作获取公参
        Pairing bp = PairingFactory.getPairing(config.getPairingParametersFileName());
        Element one = bp.getZr().newOneElement().getImmutable();
        Properties ctProp = accessstruct.Tool.loadPropFromFile(config.getCtFileName());
        Properties UserSKProp = accessstruct.Tool.loadPropFromFile(config.getUserSKFileName());

//        AccessControlParameter test = LSSSLW10Engine.generateAccessControl(AccessPolicy ,rhos);
        //计算mui，\sum {muiMi} = (1,0,...0);
        Map<String, Element> mui = LSSSPolicyEngine.reconstructOmegas(bp,userAttList,test);

        String C_0String = ctProp.getProperty("C_0");
        Element C_0 = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(C_0String)).getImmutable();

        String KString = UserSKProp.getProperty("K");
        Element K = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(KString)).getImmutable();//
        String L_0_String = UserSKProp.getProperty("L_0");
        Element L_0 = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(L_0_String)).getImmutable();//
        String L_1_String = UserSKProp.getProperty("L_1");
        Element L_1 = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(L_1_String)).getImmutable();//
        String C_1String = ctProp.getProperty("C_1");
        Element C_1 = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(C_1String)).getImmutable();//

        Element E = bp.pairing(L_0, C_0).mul(bp.pairing(K, C_1)).getImmutable();
        Element F = bp.getGT().newOneElement().getImmutable();;

        for(String att : userAttList){
            //获取密钥
            String K_i_String = UserSKProp.getProperty("K_"+att);
            Element K_i = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(K_i_String)).getImmutable();
            //获取密文
            String C_i_1String = ctProp.getProperty("C_" + att + "_1" );
            Element C_i_1 = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(C_i_1String)).getImmutable();
            String C_i_2String = ctProp.getProperty("C_" + att + "_2" );
            Element C_i_2 = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(C_i_2String)).getImmutable();
            String C_i_3String = ctProp.getProperty("C_" + att + "_3" );
            Element C_i_3 = bp.getG1().newElementFromBytes(Base64.getDecoder().decode(C_i_3String)).getImmutable();

            F = F.mul(bp.pairing(L_1, C_i_1).mul(bp.pairing(L_0, C_i_2)).mul(bp.pairing(K_i, C_i_3)).powZn(mui.get(att))).getImmutable();
        }
        Element D = E.div(F).getImmutable();

        String CString = ctProp.getProperty("C");
        Element C = bp.getGT().newElementFromBytes(Base64.getDecoder().decode(CString)).getImmutable();
        Element m = C.div(D).getImmutable();
        System.out.println("解密结束");
        return D;
    }
    public static Element localDecrypt(ConfigFileName config,Element midCt)
            throws Exception {
        System.out.println("本地解密开始");
        Pairing bp = PairingFactory.getPairing(config.getPairingParametersFileName());
        Properties UserSKProp = accessstruct.Tool.loadPropFromFile(config.getUserSKFileName());

        String thetaString = UserSKProp.getProperty("theta");
        Element theta = bp.getGT().newElementFromBytes(Base64.getDecoder().decode(thetaString)).getImmutable();
        Properties ctProp = accessstruct.Tool.loadPropFromFile(config.getCtFileName());
        String CString = ctProp.getProperty("C");
        Element C = bp.getGT().newElementFromBytes(Base64.getDecoder().decode(CString)).getImmutable();

        return C.div(midCt.powZn(theta));

    }
    public static void test() throws Exception {

        ConfigFileName config = new ConfigFileName();
        int [][] accessPolicy = ParserUtils.GenerateAccessPolicy(AccessPolicyExamples.access_policy_example_1);
        //属性值
        String[] rhos = ParserUtils.GenerateRhos(AccessPolicyExamples.access_policy_example_1);

        AccessControlParameter test = LSSSLW10Engine.generateAccessControl(accessPolicy ,rhos);

        //用户的身份和属性
        String userID = "Bob";
        String[] userAttList = AccessPolicyExamples.access_policy_example_1_satisfied_1;
//        String[] userAttList = AccessPolicyExamples.test3satisfied;
        //机构初始化步骤
        int aaNums = 1;
        CAsetup(config );
        AAsetup(config);
        //密钥生成步骤
        //该过程已包含属性盲化和属性认证密钥生成
        AAkeygen(config, userID, userAttList);
        //生成群上的随机元素作为明文进行加密
        Element message =PairingFactory.getPairing(config.getPairingParametersFileName()).getGT().newRandomElement().getImmutable();
        //加密
        Encrypt(config, userAttList,  userID, aaNums,accessPolicy,rhos,message,test);

        Element res = Decrypt(config, userAttList, userID, aaNums, accessPolicy,rhos,test);
        Element result = localDecrypt(config, res);
        System.out.println("明文消息:" + message);
        System.out.println("解密结果:" + result);
        if (message.isEqual(result)) {
            System.out.println("成功解密！");
        }else{
            System.out.println("解密失败！");
        }


    }
    /**
     * main函数
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        test();
//        test1();
    }
}
